//
//  A*.cpp
//  Taquin
//
//  Created by Benjamin Bouachour on 12/08/11.
//  Copyright 2011 Epitech. All rights reserved.
//

#include "AStar.h"
#include <time.h>
static int uniqueIdOpen = 0;
static int uniqueIdClose = 0;

Stats::Stats()
{
    node_created = 1;
    node_in_open_list = 0;
    nb_node_visited = 0;
    nb_coup = 0;
}

void Stats::affAll()
{
    cout << "total_node visited " << nb_node_visited << endl;
    cout << "nb_coup " << nb_coup << endl;
    cout << "node_created " << node_created << endl;
}

void Stats::incrementNodeInOpenList()
{
    node_in_open_list++;
    if (node_in_open_list > max_node_in_open_list)
        max_node_in_open_list = node_in_open_list;
}

AStar::AStar(Etat *_initial, const Etat *_final)
{
    heuristique = NULL;
    initial = new Etat(*_initial);
    final = new Etat(*_final);
}

AStar::AStar(Etat *initial, const Etat *final, string heu)
{
	cout << heu << endl;

	if (heu == "MANHATTAN")
		AStar(initial, final, MANAHTAN);
	if (heu ==  "EUCLIDIENNE")
		AStar(initial, final, EUCLIDIENNE);
		if (heu == "MY_HEURISTIQUE")
		AStar(initial, final, MYHEURISTIQUE);

}


AStar::AStar(Etat *_initial, const Etat *_final, int heu)
{    
    heuristique = NULL;
    final = new Etat(*_final);
    initial = new Etat(*_initial);
    this->constructHeuristique(heu);
}

AStar::AStar(Etat *_initial, const Etat *_final, IHeuristique *_heuristique)
{
    initial = new Etat(*_initial);
    heuristique = _heuristique;
    final = new Etat(*_final);
}

AStar::~AStar()
{
    if (heuristique)
        delete heuristique;
    if (final)
        delete final;
    if (initial)
        delete initial;
        
    
//    delete open_list;
    //delete close_list;
}

const a_list &AStar::getCloseList() const
{
    return close_list;
}


const void AStar::constructHeuristique(int heu)
{
	cout << heu << endl;
    if (heu == MANAHTAN)
        heuristique = new Manathan(final);
	else if (heu == EUCLIDIENNE)
		heuristique = new Euclidienne(final);
	else if (heu == MYHEURISTIQUE)
		heuristique = new MyHeuristique(final);
}

a_list::iterator AStar::callStrategie()
{
    
    a_list::iterator res = open_list.begin();
    if (open_list.size() <= 0)
        return res;
    a_list::iterator it = open_list.begin();
    int qual = it->second->getQualite();
    for (++it; it != open_list.end(); ++it)
    {
        if (it->second->getQualite() < qual)
        {
            res = it;
            qual = res->second->getQualite();
        }
    }
    return res;    
}

a_list::iterator AStar::isInCloseList(evoisins::iterator it)
{
    a_list::iterator itClose;
    for(itClose = close_list.begin(); itClose != close_list.end(); ++itClose)
        if (*(it->second) == *(itClose->second))
            return itClose;
    return itClose;
}

a_list::iterator AStar::isInOpenList(evoisins::iterator it)
{
    a_list::iterator itOpen;
    for(itOpen = open_list.begin(); itOpen != open_list.end(); ++itOpen)
        if (*(it->second) == *(itOpen->second))
            return itOpen;
    return itOpen;

}

a_list::iterator AStar::isInCloseList(Etat *it)
{
    a_list::iterator itClose;
    for(itClose = close_list.begin(); itClose != close_list.end(); ++itClose)
        if (*(it) == *(itClose->second))
            return itClose;
    return itClose;
}

a_list::iterator AStar::isInOpenList(Etat *it)
{
    a_list::iterator itOpen;
    for(itOpen = open_list.begin(); itOpen != open_list.end(); ++itOpen)
        if (*(it) == *(itOpen->second))
            return itOpen;
    return itOpen;
    
}


const int AStar::getQualite(a_list::iterator it)
{
    cout << "AStar::getqQualité " <<  endl;
    Etat *etat = it->second;
    return this->getQualite(etat);
}

const int AStar::getQualite(Etat *etat)
{
    cout << "AStar::getqQualité " <<  endl;
    int rep = etat->getQualite();
    cout << "reponse de Etat::getQualité " << rep << endl;
    if (rep == -1) {
        cout << "rep == -1 " <<  endl;
        rep = heuristique->calculHeuristique(etat);
        cout << "reponse nouvelle qualité " << rep << endl;
        etat->setQualite(rep);
    }
    return rep;
}

const void AStar::initInitialNode()
{
//    initial->setCost(0);
    heuristique->calculGCost(NULL, initial);
    heuristique->calculHCost(NULL,initial);
    heuristique->calculHeuristique(initial);
    initial->fillListVoisins();
    open_list[0] = initial;
}


const bool AStar::run_Ia()
{
    cout << "Enter RunIA" <<"" << endl;
    initInitialNode();
    bool success = false;
    AffResult aff;
    clock_t start = clock();
    Stats stat;
    
    
    while (open_list.size() > 0 && success == false)
    {
        a_list::iterator currentIt = callStrategie();
        Etat *current = currentIt->second;
        
//        cout << "Courrant" << endl;
//        aff.affEtat(current);
//        if (current->getParent() != NULL) {
//            cout << "Parent" << endl;
//            aff.affEtat(current->getParent());
//            
//        }
        if (current == NULL)
            break;
        if (*current == *final)
        {
            success = true;
            delete final;
            final = current;
        }
        else
        {
            current->fillListVoisins();
            evoisins child = current->getListVoisins();
            close_list.insert(make_pair<int, Etat*>(++uniqueIdClose, current));
            open_list.erase(currentIt);
            stat.node_in_open_list--;
            stat.nb_node_visited++;
            
            for(evoisins::iterator currentChild = child.begin(); currentChild != child.end(); ++currentChild)
            {
                stat.node_created++;
                heuristique->calculGCost(current, currentChild->second);
                heuristique->calculHCost(current, currentChild->second);
                heuristique->calculHeuristique(currentChild->second);
                a_list::iterator itClose =  isInCloseList(currentChild);
                a_list::iterator itOpen = isInOpenList(currentChild);
                if (itOpen == open_list.end() && itClose == close_list.end()) 
                {
                    currentChild->second->setParent(current);
                    stat.incrementNodeInOpenList();
                    open_list.insert(make_pair<int, Etat*>(++uniqueIdOpen, currentChild->second));
                }
                else
                {
                    a_list::iterator it = itClose == close_list.end() ? itOpen : itClose;
//                    int comp = currentChild->second->getQualite() - it->second->getQualite();
//                    if (comp < 0)
					if ( it->second->getQualite() > currentChild->second->getQualite())
                    {
//                        it->second->setCost(it->second->getCost() + comp);
                          it->second->setCost(currentChild->second->getCost());
//                        it->second->setQualite(it->second->getQualite() + comp);
						   heuristique->calculHeuristique(it->second);
                        it->second->setParent(current);
                        a_list::iterator closeIt;
                        if (isInCloseList(it) != close_list.end()) 
                        {
                            stat.incrementNodeInOpenList();
                            open_list.insert(make_pair<int, Etat *>(++uniqueIdOpen, it->second));
                            close_list.erase(it);
                        }
                    }
                    delete currentChild->second;
                }
            }
//			aff.affEtat(current);
        }
    }
    clock_t end = clock() - start;
    if (success == true) {
        int res = aff.AffStar(final);
        cout << "nombre de coup = " << res <<endl;
    }
    cout << "Resolution en " << end  << endl;
	cout << "soit : " << end / CLOCKS_PER_SEC << "sec" << endl;
    stat.affAll();
	cout << "Euclidiene * 10000 ->heuristique : ssqrt(powf(coord_f.first - coord_v.first , 2) + powf(coord_f.second - coord_v.second, 2)) * 10000;" << endl;
//    for (a_list::iterator it = close_list.begin(); it != close_list.end(); ++it) {
//        delete it->second;
//    }
//    for (a_list::iterator it = open_list.begin(); it != open_list.end(); ++it) {
//        delete it->second;
//    }
    return false;
}


void AStar::reconstructPath(Etat *etat)
{
//    while (etat != NULL)
//    {
//        res.affEtat(etat);
//        etat = etat->getParent();
//    }

}







//
//
//AffResult aff;
//bool success = false;
//int i = 0;
//Etat *currentState = initial;
//currentState->setQualite(0);
//open_list[0] = currentState;
//while (open_list.size() > 0 && !success)
//{
//    cout << "récupere l'etat courant dans open_list" << endl;
//    a_list::iterator currentState = callStrategie();
//    if (currentState == open_list.end())
//    {
//        cout << " " << "on sort car currentState == nd of openList"<< endl;
//        return false;
//    }
//    cout << "Etat courant " << endl;
//    //        aff.affEtat(currentState->second);
//    if (*(currentState->second) == *final) 
//    {
//        cout << "Etat courant est == au final" << endl;
//        success = true;
//    }
//    else
//    { 
//        
//        cout << "boucle " << i << " close_list = " << endl;
//        AffResult::AffStar(close_list); 
//        
//        Etat *copie = new Etat(currentState->second);
//        pair<int, Etat*> listElement = pair<int, Etat*>((int)open_list.size(), copie);
//        close_list.insert(listElement);
//        open_list.erase(currentState);
//        
//        //            cout << "copie des elements de l'open list vers la close list" << endl;
//        //            cout << "apres copie " << i << " open_list = "<< endl;
//        //            AffResult::AffStar(open_list);
//        //            /*Copie le current de open->close par copie Attention possibilité d'erreur du à l'effacement de l'iterator*/
//        //            cout << "apres copie " << i << " close_list = " << endl;
//        //            AffResult::AffStar(close_list); 
//        //            currentState = close_list.find(listElement.first);
//        
//        
//        /*On trie les voisins*/
//        listElement.second->fillListVoisins();
//        evoisins currentVoisins = listElement.second->getListVoisins();
//        evoisins::iterator it;
//        for (it = currentVoisins.begin(); it != currentVoisins.end(); ++it)
//        {
//            getQualite(listElement.second);
//            if (isInCloseList(it) == close_list.end() && isInOpenList(it) == open_list.end())
//                open_list.insert(pair<int, Etat*>((int)open_list.size(), it->second));
//            /*A refaire car je dois itérer alors que je devrais atteindre directement*/
//            if (isInCloseList(it) != close_list.end())
//                continue;
//            // comparer la qualité de celui dans la liste
//            
//            a_list::iterator itOpen = isInOpenList(it);
//            if (itOpen == open_list.end())
//                open_list[(int)open_list.size()] = it->second;
//            else
//            {
//                if (getQualite(currentState->second))
//                {
//                    *(itOpen->second) = *(it->second);
//                }
//            }
//        }
//        
//        
//    }
//    cout << open_list.size() << endl;
//    i++;
//    }
//    if (success == true)
//    return true;
//    return false;
//
//    

